package com.smart.system.domain;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AreaLocation {
    /**
     * 标识
     */
    @Schema(description = "后端用于标识,前端不用管此字段")
    private String number;

    /**
     * 经度
     */
    @Schema(description = "经度")
    private String longitude;
    /**
     * 纬度
     */
    @Schema(description = "纬度")
    private String latitude;

    /** 时间节点 **/
    private Date time;

    /**
     * 判断给定的经纬度坐标是否在多边形区域内。
     *
     * @param point 经纬度坐标
     * @param polygon 多边形顶点列表
     * @return 如果点在多边形内部返回 true，否则返回 false
     */
    public static boolean isPointInPolygon(AreaLocation point, List<AreaLocation> polygon) {
        if (polygon == null || polygon.size() < 3) {
            return false; // 不是多边形
        }

        double px = Double.parseDouble(point.getLongitude());
        double py = Double.parseDouble(point.getLatitude());

        int n = polygon.size();
        boolean inside = false;

        // 提前转换多边形顶点的坐标
        double[] xCoords = new double[n];
        double[] yCoords = new double[n];
        for (int i = 0; i < n; i++) {
            xCoords[i] = Double.parseDouble(polygon.get(i).getLongitude());
            yCoords[i] = Double.parseDouble(polygon.get(i).getLatitude());
        }

        for (int i = 0, j = n - 1; i < n; j = i++) {
            double x1 = xCoords[i];
            double y1 = yCoords[i];
            double x2 = xCoords[j];
            double y2 = yCoords[j];

            // 检查射线是否与多边形边界相交
            if ((y1 > py) != (y2 > py) && px < (x2 - x1) * (py - y1) / (y2 - y1) + x1) {
                inside = !inside;
            }
        }

        return inside;
    }

    // 地球半径，单位为米
    private static final double EARTH_RADIUS = 6378137.0;
    // 将度转换为弧度的常量
    private static final double RADIANS_PER_DEGREE = Math.PI / 180.0;

    // 计算两个 GPS 坐标点之间的距离
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        // 将纬度从度转换为弧度
        double radLat1 = lat1 * RADIANS_PER_DEGREE;
        double radLat2 = lat2 * RADIANS_PER_DEGREE;

        // 计算纬度差和经度差的弧度值
        double deltaLat = radLat1 - radLat2;
        double deltaLon = (lon1 - lon2) * RADIANS_PER_DEGREE;

        // 计算 Haversine 公式中的各个部分
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);

        // 计算 c = 2 * atan2(√a, √(1−a))
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        // 返回两点之间的距离（单位：米）
        return EARTH_RADIUS * c;
    }

    /**
     * 过滤 GPS 坐标点
     * @param lastPoint 上一个点
     * @param currentPoint 当前点
     * @param threshold 阈值
     * @return
     */
    public static AreaLocation filterGpsPoint(AreaLocation lastPoint, AreaLocation currentPoint, double threshold) {
        // 计算上一个点和当前点之间的距离
        double distance = calculateDistance(Double.parseDouble(lastPoint.getLatitude())
                , Double.parseDouble(lastPoint.getLongitude())
                , Double.parseDouble(currentPoint.getLatitude())
                , Double.parseDouble(currentPoint.getLongitude()));
        // 如果距离大于阈值，则返回当前点
        if (distance > threshold) {
            return currentPoint;
        } else {
            // 如果点距离小于阈值，则保留上一个点
            return lastPoint;
        }
    }

    /**
     * 获取给定经纬度范围内指定半径的圆形坐标范围
     *
     * @param location  经纬度对象
     * @param radius    半径，单位：米
     * @param steps     生成的点的数量
     * @return 圆形范围内的经纬度集合
     */
    public static List<AreaLocation> getCircleCoordinates(AreaLocation location, double radius, int steps) {
        List<AreaLocation> coordinates = new ArrayList<>();
        double angularDistance = radius / EARTH_RADIUS;
        double centerLatRad = Math.toRadians(Double.parseDouble(location.latitude));
        double centerLngRad = Math.toRadians(Double.parseDouble(location.longitude));

        for (int i = 0; i < steps; i++) {
            double bearing = 2 * Math.PI * i / steps;
            double latRad = Math.asin(Math.sin(centerLatRad) * Math.cos(angularDistance) +
                    Math.cos(centerLatRad) * Math.sin(angularDistance) * Math.cos(bearing));
            double lngRad = centerLngRad + Math.atan2(Math.sin(bearing) * Math.sin(angularDistance) * Math.cos(centerLatRad),
                    Math.cos(angularDistance) - Math.sin(centerLatRad) * Math.sin(latRad));

            double lat = Math.toDegrees(latRad);
            double lng = Math.toDegrees(lngRad);

            coordinates.add(new AreaLocation(null,String.valueOf(lat), String.valueOf(lng),null));
        }

        return coordinates;
    }


    /**
     * 统计从区域A到区域B来回的次数
     *+++++++++++++
     * @param trajectory 轨迹记录
     * @param regionA 区域A的顶点列表
     * @param regionB 区域B的顶点列表
     * @return 回来次数
     */
    public static int countRoundTrips(List<AreaLocation> trajectory, List<AreaLocation> regionA, List<AreaLocation> regionB) {
        boolean inRegionA = false;
        boolean inRegionB = false;
        int roundTrips = 0;

        for (AreaLocation point : trajectory) {
            boolean isInA = AreaLocation.isPointInPolygon(point, regionA);
            boolean isInB = AreaLocation.isPointInPolygon(point, regionB);

            if (isInA && !inRegionA) {
                inRegionA = true;
                inRegionB = false;
            } else if (isInB && !inRegionB) {
                if (inRegionA) {
                    roundTrips++;
                }
                inRegionB = true;
                inRegionA = false;
            } else if (!isInA && !isInB) {
                inRegionA = false;
                inRegionB = false;
            }
        }

        return roundTrips;
    }


    public static double calculateTotalDistance(List<AreaLocation> trajectory) {
        if (trajectory == null || trajectory.size() < 2) {
            return 0.0; // 如果轨迹点少于2个，则无法计算距离
        }

        double totalDistance = 0.0;

        for (int i = 0; i < trajectory.size() - 1; i++) {
            AreaLocation point1 = trajectory.get(i);
            AreaLocation point2 = trajectory.get(i + 1);

            double distance = calculateDistance(
                    Double.parseDouble(point1.getLatitude()),
                    Double.parseDouble(point1.getLongitude()),
                    Double.parseDouble(point2.getLatitude()),
                    Double.parseDouble(point2.getLongitude())
            );

            totalDistance += distance;
        }

        // 将总距离从米转换为千米
        return totalDistance / 1000.0;
    }

}